#include "krtpch.h"

#include "../../include/Hit/Primitive.h"
#include "../../include/Sdf/Sdf.h"

namespace krt {

	////////////////////////////////////////
	/// SdfSphere
	////////////////////////////////////////

	bool SdfSphere::Intersect(const Ray& r, float& tHit, float& epsilon, Intersection& dg, SdfRecordType& sdfRec) const
	{
		//// Transform Ray to object space
		Ray ray = worldToObject->Trans(r);
		//// Compute quadratic sphere coefficients 'a, b, c'
		float a = glm::dot(ray.direct, ray.direct);
		float b = 2 * glm::dot(ray.direct, ray.origin);
		float c = glm::dot(ray.origin, ray.origin) - radius * radius;
		float discriminant = b * b - 4.0f * a * c;
		if (discriminant <= .0)return false;
		// Solve high-precession quadratic equation for t values
		float rootD = std::sqrtf(discriminant);
		float q;
		if (b < 0)q = -.5f * (b - rootD);
		else q = -.5f * (b + rootD);
		float t0 = q / a;
		float t1 = c / q;
		if (t0 > t1)std::swap(t0, t1);
		if (t0 > ray.maxt || t1 < ray.mint)return false;
		float thit = t0;
		if (t0 < ray.mint)
		{
			thit = t1;
			if (thit > ray.maxt)return false;
		}
		// Get hit point and normal
		glm::vec3 pHit = ray.PointTo(thit);
		if (pHit.x == 0.f && pHit.y == 0.f)pHit.x = 1e-5f * radius;
		using NormalV = typename glm::vec3;
		NormalV nor = glm::normalize(pHit);
		// Find parametric representation of hit
		using AngleF = typename float;
		AngleF phi = atan2f(pHit.y, pHit.x);
		if (phi < 0.) phi += 2.f * glm::pi<float>();
		AngleF theta = acosf(glm::clamp(pHit.z / radius, -1.f, 1.f));
		float u = phi / phiMax;
		float v = theta * 0.5f + 0.5f;
		// Set Intersection and other params
		const Transform& o2w = *objectToWorld;
		tHit = thit;
		epsilon = 5e-4f * thit;
		dg.hitPoint = o2w.Trans(pHit);
		dg.uv = { u,v };
		dg.normal = o2w.TransNormal(nor);
		return true;
	}

	bool SdfSphere::Sdf(const glm::vec3& p, float& sdfResult) const
	{
		return false;
	}

	BBox SdfSphere::GetBBox() const
	{
		return BBox(
			{ -radius,-radius ,-radius },
			{ radius ,radius ,radius }
		);
	}

	glm::vec2 SdfSphere::GetSphereUV(const glm::vec3& normal)
	{
		float cosY = normal.y;
		float cosX = normal.x / sqrt(normal.x * normal.x + normal.z * normal.z);
		constexpr float pi_div = 1.0f / glm::pi<float>();
		return {
			(normal.z > 0.0f) ?
				(acos(cosX) * pi_div * 0.5f) :
				(1 - acos(cosX) * pi_div * 0.5f) ,
			acos(cosY) * pi_div
		};
	}

	////////////////////////////////////////
	/// SdfTriangle
	////////////////////////////////////////

	bool SdfTriangle::Intersect(const Ray& r, float& tHit, float& epsilon, Intersection& dg, SdfRecordType& sdfRec) const
	{
		return false;
	}

	bool SdfTriangle::Sdf(const glm::vec3& p, float& sdfResult) const
	{
		return false;
	}

	BBox SdfTriangle::GetBBox() const
	{
		return BBox();
	}

	////////////////////////////////////////
	/// SdfUnion
	////////////////////////////////////////

	bool SdfUnion::Intersect(const Ray& r, float& tHit, float& epsilon, Intersection& dg, SdfRecordType& sdfRec) const
	{
		return false;
	}

	bool SdfUnion::Sdf(const glm::vec3& p, float& sdfResult) const
	{
		return false;
	}

	BBox SdfUnion::GetBBox() const
	{
		return BBox();
	}

}